home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Mac / Demo / waste / htmled.py next >
Encoding:
Text File  |  2000-06-23  |  20.5 KB  |  828 lines

  1. # A minimal text editor.
  2. #
  3. # To be done:
  4. # - Functionality: find, etc.
  5.  
  6. from Menu import DrawMenuBar
  7. from FrameWork import *
  8. import Win
  9. import Qd
  10. import Res
  11. import Fm
  12. import waste
  13. import WASTEconst
  14. import Scrap
  15. import os
  16. import macfs
  17. import regsub
  18. import string
  19. import htmllib
  20.  
  21. WATCH = Qd.GetCursor(4).data
  22.  
  23. LEFTMARGIN=0
  24.  
  25. UNDOLABELS = [ # Indexed by WEGetUndoInfo() value
  26.     None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
  27.     
  28. # Style and size menu. Note that style order is important (tied to bit values)
  29. STYLES = [
  30.     ("Bold", "B"), ("Italic", "I"), ("Underline", "U"), ("Outline", "O"),
  31.     ("Shadow", ""), ("Condensed", ""), ("Extended", "")
  32.     ]
  33. SIZES = [ 9, 10, 12, 14, 18, 24]
  34.  
  35. # Sizes for HTML tag types
  36. HTML_SIZE={
  37.     'h1': 18,
  38.     'h2': 14
  39. }
  40.     
  41. BIGREGION=Qd.NewRgn()
  42. Qd.SetRectRgn(BIGREGION, -16000, -16000, 16000, 16000)
  43.  
  44. class WasteWindow(ScrolledWindow):
  45.     def open(self, path, name, data):
  46.         self.path = path
  47.         self.name = name
  48.         r = windowbounds(400, 400)
  49.         w = Win.NewWindow(r, name, 1, 0, -1, 1, 0x55555555)
  50.         self.wid = w
  51.         vr = LEFTMARGIN, 0, r[2]-r[0]-15, r[3]-r[1]-15
  52.         dr = (0, 0, vr[2], 0)
  53.         Qd.SetPort(w)
  54.         Qd.TextFont(4)
  55.         Qd.TextSize(9)
  56.         flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoOutlineHilite | \
  57.             WASTEconst.weDoMonoStyled | WASTEconst.weDoUndo
  58.         self.ted = waste.WENew(dr, vr, flags)
  59.         self.ted.WEInstallTabHooks()
  60.         style, soup = self.getstylesoup(self.path)
  61.         self.ted.WEInsert(data, style, soup)
  62.         self.ted.WESetSelection(0,0)
  63.         self.ted.WECalText()
  64.         self.ted.WEResetModCount()
  65.         w.DrawGrowIcon()
  66.         self.scrollbars()
  67.         self.do_postopen()
  68.         self.do_activate(1, None)
  69.         
  70.     def getstylesoup(self, pathname):
  71.         if not pathname:
  72.             return None, None
  73.         oldrf = Res.CurResFile()
  74.         try:
  75.             rf = Res.OpenResFile(self.path)
  76.         except Res.Error:
  77.             return None, None
  78.         try:
  79.             hstyle = Res.Get1Resource('styl', 128)
  80.             hstyle.DetachResource()
  81.         except Res.Error:
  82.             hstyle = None
  83.         try:
  84.             hsoup = Res.Get1Resource('SOUP', 128)
  85.             hsoup.DetachResource()
  86.         except Res.Error:
  87.             hsoup = None
  88.         Res.CloseResFile(rf)
  89.         Res.UseResFile(oldrf)
  90.         return hstyle, hsoup
  91.                 
  92.     def do_idle(self, event):
  93.         (what, message, when, where, modifiers) = event
  94.         Qd.SetPort(self.wid)
  95.         self.ted.WEIdle()    
  96.         if self.ted.WEAdjustCursor(where, BIGREGION):
  97.             return
  98.         Qd.SetCursor(Qd.qd.arrow)
  99.         
  100.     def getscrollbarvalues(self):
  101.         dr = self.ted.WEGetDestRect()
  102.         vr = self.ted.WEGetViewRect()
  103.         vx = self.scalebarvalue(dr[0], dr[2], vr[0], vr[2])
  104.         vy = self.scalebarvalue(dr[1], dr[3], vr[1], vr[3])
  105.         return vx, vy
  106.         
  107.     def scrollbar_callback(self, which, what, value):
  108.         if which == 'y':
  109.             #
  110.             # "line" size is minimum of top and bottom line size
  111.             #
  112.             topline_off,dummy = self.ted.WEGetOffset((1,1))
  113.             topline_num = self.ted.WEOffsetToLine(topline_off)
  114.             toplineheight = self.ted.WEGetHeight(topline_num, topline_num+1)
  115.  
  116.             botlinepos = self.ted.WEGetViewRect()[3]            
  117.             botline_off, dummy = self.ted.WEGetOffset((1, botlinepos-1))
  118.             botline_num = self.ted.WEOffsetToLine(botline_off)
  119.             botlineheight = self.ted.WEGetHeight(botline_num, botline_num+1)
  120.             
  121.             if botlineheight == 0:
  122.                 botlineheight = self.ted.WEGetHeight(botline_num-1, botline_num)
  123.             if botlineheight < toplineheight:
  124.                 lineheight = botlineheight
  125.             else:
  126.                 lineheight = toplineheight
  127.             if lineheight <= 0:
  128.                 lineheight = 1
  129.             #
  130.             # Now do the command.
  131.             #
  132.             if what == 'set':
  133.                 height = self.ted.WEGetHeight(0, 0x3fffffff)
  134.                 cur = self.getscrollbarvalues()[1]
  135.                 delta = (cur-value)*height/32767
  136.             if what == '-':
  137.                 delta = lineheight
  138.             elif what == '--':
  139.                 delta = (self.ted.WEGetViewRect()[3]-lineheight)
  140.                 if delta <= 0:
  141.                     delta = lineheight
  142.             elif what == '+':
  143.                 delta = -lineheight
  144.             elif what == '++':
  145.                 delta = -(self.ted.WEGetViewRect()[3]-lineheight)
  146.                 if delta >= 0:
  147.                     delta = -lineheight
  148.             self.ted.WEScroll(0, delta)
  149.         else:
  150.             if what == 'set':
  151.                 return # XXXX
  152.             vr = self.ted.WEGetViewRect()
  153.             winwidth = vr[2]-vr[0]
  154.             if what == '-':
  155.                 delta = winwidth/10
  156.             elif what == '--':
  157.                 delta = winwidth/2
  158.             elif what == '+':
  159.                 delta = -winwidth/10
  160.             elif what == '++':
  161.                 delta = -winwidth/2
  162.             self.ted.WEScroll(delta, 0)
  163.         # Pin the scroll
  164.         l, t, r, b = self.ted.WEGetDestRect()
  165.         vl, vt, vr, vb = self.ted.WEGetViewRect()
  166.         if t > 0 or l > 0:
  167.             dx = dy = 0
  168.             if t > 0: dy = -t
  169.             if l > 0: dx = -l
  170.             self.ted.WEScroll(dx, dy)
  171.         elif b < vb:
  172.             self.ted.WEScroll(0, vb-b)
  173.  
  174.         
  175.     def do_activate(self, onoff, evt):
  176.         Qd.SetPort(self.wid)
  177.         ScrolledWindow.do_activate(self, onoff, evt)
  178.         if onoff:
  179.             self.ted.WEActivate()
  180.             self.parent.active = self
  181.             self.parent.updatemenubar()
  182.         else:
  183.             self.ted.WEDeactivate()
  184.  
  185.     def do_update(self, wid, event):
  186.         region = wid.GetWindowPort().visRgn
  187.         if Qd.EmptyRgn(region):
  188.             return
  189.         Qd.EraseRgn(region)
  190.         self.ted.WEUpdate(region)
  191.         self.updatescrollbars()
  192.         
  193.     def do_postresize(self, width, height, window):
  194.         l, t, r, b = self.ted.WEGetViewRect()
  195.         vr = (l, t, l+width-15, t+height-15)
  196.         self.ted.WESetViewRect(vr)
  197.         Win.InvalRect(vr)
  198.         ScrolledWindow.do_postresize(self, width, height, window)
  199.         
  200.     def do_contentclick(self, local, modifiers, evt):
  201.         (what, message, when, where, modifiers) = evt
  202.         self.ted.WEClick(local, modifiers, when)
  203.         self.updatescrollbars()
  204.         self.parent.updatemenubar()
  205.  
  206.     def do_char(self, ch, event):
  207.         self.ted.WESelView()
  208.         (what, message, when, where, modifiers) = event
  209.         self.ted.WEKey(ord(ch), modifiers)
  210.         self.updatescrollbars()
  211.         self.parent.updatemenubar()
  212.         
  213.     def close(self):
  214.         if self.ted.WEGetModCount():
  215.             save = EasyDialogs.AskYesNoCancel('Save window "%s" before closing?'%self.name, 1)
  216.             if save > 0:
  217.                 self.menu_save()
  218.             elif save < 0:
  219.                 return
  220.         if self.parent.active == self:
  221.             self.parent.active = None
  222.         self.parent.updatemenubar()
  223.         del self.ted
  224.         self.do_postclose()
  225.         
  226.     def menu_save(self):
  227.         if not self.path:
  228.             self.menu_save_as()
  229.             return # Will call us recursively
  230.         #
  231.         # First save data
  232.         #
  233.         dhandle = self.ted.WEGetText()
  234.         data = dhandle.data
  235.         fp = open(self.path, 'wb')  # NOTE: wb, because data has CR for end-of-line
  236.         fp.write(data)
  237.         if data[-1] <> '\r': fp.write('\r')
  238.         fp.close()
  239.         #
  240.         # Now save style and soup
  241.         #
  242.         oldresfile = Res.CurResFile()
  243.         try:
  244.             rf = Res.OpenResFile(self.path)
  245.         except Res.Error:
  246.             Res.CreateResFile(self.path)
  247.             rf = Res.OpenResFile(self.path)
  248.         styles = Res.Resource('')
  249.         soup = Res.Resource('')
  250.         self.ted.WECopyRange(0, 0x3fffffff, None, styles, soup)
  251.         styles.AddResource('styl', 128, '')
  252.         soup.AddResource('SOUP', 128, '')
  253.         Res.CloseResFile(rf)
  254.         Res.UseResFile(oldresfile)
  255.         
  256.         self.ted.WEResetModCount()
  257.         
  258.     def menu_save_as(self):
  259.         fss, ok = macfs.StandardPutFile('Save as:')
  260.         if not ok: return
  261.         self.path = fss.as_pathname()
  262.         self.name = os.path.split(self.path)[-1]
  263.         self.wid.SetWTitle(self.name)
  264.         self.menu_save()
  265.         
  266.     def menu_insert(self, fp):
  267.         self.ted.WESelView()
  268.         data = fp.read()
  269.         self.ted.WEInsert(data, None, None)
  270.         self.updatescrollbars()
  271.         self.parent.updatemenubar()
  272.         
  273.     def menu_insert_html(self, fp):
  274.         import htmllib
  275.         import formatter
  276.         f = formatter.AbstractFormatter(self)
  277.         
  278.         # Remember where we are, and don't update
  279.         Qd.SetCursor(WATCH)
  280.         start, dummy = self.ted.WEGetSelection()
  281.         self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 1)
  282.  
  283.         self.html_init()
  284.         p = MyHTMLParser(f)
  285.         p.feed(fp.read())
  286.         
  287.         # Restore updating, recalc, set focus
  288.         dummy, end = self.ted.WEGetSelection()
  289.         self.ted.WECalText()
  290.         self.ted.WESetSelection(start, end)
  291.         self.ted.WESelView()
  292.         self.ted.WEFeatureFlag(WASTEconst.weFInhibitRecal, 0)
  293.         Win.InvalRect(self.ted.WEGetViewRect())
  294.  
  295.         self.updatescrollbars()
  296.         self.parent.updatemenubar()
  297.         
  298.     def menu_cut(self):
  299.         self.ted.WESelView()
  300.         self.ted.WECut()
  301.         Scrap.ZeroScrap()
  302.         self.ted.WECut()
  303.         self.updatescrollbars()
  304.         self.parent.updatemenubar()
  305.         
  306.     def menu_copy(self):
  307.         Scrap.ZeroScrap()
  308.         self.ted.WECopy()
  309.         self.updatescrollbars()
  310.         self.parent.updatemenubar()
  311.         
  312.     def menu_paste(self):
  313.         self.ted.WESelView()
  314.         self.ted.WEPaste()
  315.         self.updatescrollbars()
  316.         self.parent.updatemenubar()
  317.         
  318.     def menu_clear(self):
  319.         self.ted.WESelView()
  320.         self.ted.WEDelete()
  321.         self.updatescrollbars()
  322.         self.parent.updatemenubar()
  323.  
  324.     def menu_undo(self):
  325.         self.ted.WEUndo()
  326.         self.updatescrollbars()
  327.         self.parent.updatemenubar()
  328.         
  329.     def menu_setfont(self, font):
  330.         font = Fm.GetFNum(font)
  331.         self.mysetstyle(WASTEconst.weDoFont, (font, 0, 0, (0,0,0)))
  332.         self.parent.updatemenubar()
  333.                 
  334.     def menu_modface(self, face):
  335.         self.mysetstyle(WASTEconst.weDoFace|WASTEconst.weDoToggleFace, 
  336.             (0, face, 0, (0,0,0)))
  337.  
  338.     def menu_setface(self, face):
  339.         self.mysetstyle(WASTEconst.weDoFace|WASTEconst.weDoReplaceFace, 
  340.             (0, face, 0, (0,0,0)))
  341.  
  342.     def menu_setsize(self, size):
  343.         self.mysetstyle(WASTEconst.weDoSize, (0, 0, size, (0,0,0)))
  344.                                 
  345.     def menu_incsize(self, size):
  346.         self.mysetstyle(WASTEconst.weDoAddSize, (0, 0, size, (0,0,0)))
  347.  
  348.     def mysetstyle(self, which, how):
  349.         self.ted.WESelView()
  350.         self.ted.WESetStyle(which, how)
  351.         self.parent.updatemenubar()
  352.                                 
  353.     def have_selection(self):
  354.         start, stop = self.ted.WEGetSelection()
  355.         return start < stop
  356.         
  357.     def can_paste(self):
  358.         return self.ted.WECanPaste()
  359.         
  360.     def can_undo(self):
  361.         which, redo = self.ted.WEGetUndoInfo()
  362.         which = UNDOLABELS[which]
  363.         if which == None: return None
  364.         if redo:
  365.             return "Redo "+which
  366.         else:
  367.             return "Undo "+which
  368.             
  369.     def getruninfo(self):
  370.         all = (WASTEconst.weDoFont | WASTEconst.weDoFace | WASTEconst.weDoSize)
  371.         dummy, mode, (font, face, size, color) = self.ted.WEContinuousStyle(all)
  372.         if not (mode & WASTEconst.weDoFont):
  373.             font = None
  374.         else:
  375.             font = Fm.GetFontName(font)
  376.         if not (mode & WASTEconst.weDoFace): fact = None
  377.         if not (mode & WASTEconst.weDoSize): size = None
  378.         return font, face, size
  379.         
  380.     #
  381.     # Methods for writer class for html formatter
  382.     #
  383.     
  384.     def html_init(self):
  385.         self.html_font = [12, 0, 0, 0]
  386.         self.html_style = 0
  387.         self.html_color = (0,0,0)
  388.         self.new_font(self.html_font)
  389.     
  390.     def new_font(self, font):
  391.         if font == None:
  392.             font = (12, 0, 0, 0)
  393.         font = map(lambda x:x, font)
  394.         for i in range(len(font)):
  395.             if font[i] == None:
  396.                 font[i] = self.html_font[i]
  397.         [size, italic, bold, tt] = font
  398.         self.html_font = font[:]
  399.         if tt:
  400.             font = Fm.GetFNum('Courier')
  401.         else:
  402.             font = Fm.GetFNum('Times')
  403.         if HTML_SIZE.has_key(size):
  404.             size = HTML_SIZE[size]
  405.         else:
  406.             size = 12
  407.         face = 0
  408.         if bold: face = face | 1
  409.         if italic: face = face | 2
  410.         face = face | self.html_style
  411.         self.ted.WESetStyle(WASTEconst.weDoFont | WASTEconst.weDoFace | 
  412.                 WASTEconst.weDoSize | WASTEconst.weDoColor,
  413.                 (font, face, size, self.html_color))
  414.         
  415.     def new_margin(self, margin, level):
  416.         self.ted.WEInsert('[Margin %s %s]'%(margin, level), None, None)
  417.         
  418.     def new_spacing(self, spacing):
  419.         self.ted.WEInsert('[spacing %s]'%spacing, None, None)
  420.             
  421.     def new_styles(self, styles):
  422.         self.html_style = 0
  423.         self.html_color = (0,0,0)
  424.         if 'anchor' in styles:
  425.             self.html_style = self.html_style | 4
  426.             self.html_color = (0xffff, 0, 0)
  427.         self.new_font(self.html_font)
  428.  
  429.     def send_paragraph(self, blankline):
  430.         self.ted.WEInsert('\r'*(blankline+1), None, None)
  431.         
  432.     def send_line_break(self):
  433.         self.ted.WEInsert('\r', None, None)
  434.         
  435.     def send_hor_rule(self, *args, **kw):
  436.         # Ignore ruler options, for now
  437.         dummydata = Res.Resource('')
  438.         self.ted.WEInsertObject('rulr', dummydata, (0,0))
  439.         
  440.     def send_label_data(self, data):
  441.         self.ted.WEInsert(data, None, None)
  442.         
  443.     def send_flowing_data(self, data):
  444.         self.ted.WEInsert(data, None, None)
  445.         
  446.     def send_literal_data(self, data):
  447.         data = regsub.gsub('\n', '\r', data)
  448.         data = string.expandtabs(data)
  449.         self.ted.WEInsert(data, None, None)
  450.         
  451. class Wed(Application):
  452.     def __init__(self):
  453.         Application.__init__(self)
  454.         self.num = 0
  455.         self.active = None
  456.         self.updatemenubar()
  457.         waste.STDObjectHandlers()
  458.         # Handler for horizontal ruler
  459.         waste.WEInstallObjectHandler('rulr', 'new ', self.newRuler)
  460.         waste.WEInstallObjectHandler('rulr', 'draw', self.drawRuler)
  461.         
  462.     def makeusermenus(self):
  463.         self.filemenu = m = Menu(self.menubar, "File")
  464.         self.newitem = MenuItem(m, "New window", "N", self.open)
  465.         self.openitem = MenuItem(m, "Open...", "O", self.openfile)
  466.         self.closeitem = MenuItem(m, "Close", "W", self.closewin)
  467.         m.addseparator()
  468.         self.saveitem = MenuItem(m, "Save", "S", self.save)
  469.         self.saveasitem = MenuItem(m, "Save as...", "", self.saveas)
  470.         m.addseparator()
  471.         self.insertitem = MenuItem(m, "Insert plaintext...", "", self.insertfile)
  472.         self.htmlitem = MenuItem(m, "Insert HTML...", "", self.inserthtml)
  473.         m.addseparator()
  474.         self.quititem = MenuItem(m, "Quit", "Q", self.quit)
  475.         
  476.         self.editmenu = m = Menu(self.menubar, "Edit")
  477.         self.undoitem = MenuItem(m, "Undo", "Z", self.undo)
  478.         self.cutitem = MenuItem(m, "Cut", "X", self.cut)
  479.         self.copyitem = MenuItem(m, "Copy", "C", self.copy)
  480.         self.pasteitem = MenuItem(m, "Paste", "V", self.paste)
  481.         self.clearitem = MenuItem(m, "Clear", "", self.clear)
  482.         
  483.         self.makefontmenu()
  484.         
  485.         # Groups of items enabled together:
  486.         self.windowgroup = [self.closeitem, self.saveitem, self.saveasitem,
  487.             self.editmenu, self.fontmenu, self.facemenu, self.sizemenu,
  488.             self.insertitem]
  489.         self.focusgroup = [self.cutitem, self.copyitem, self.clearitem]
  490.         self.windowgroup_on = -1
  491.         self.focusgroup_on = -1
  492.         self.pastegroup_on = -1
  493.         self.undo_label = "never"
  494.         self.ffs_values = ()
  495.         
  496.     def makefontmenu(self):
  497.         self.fontmenu = Menu(self.menubar, "Font")
  498.         self.fontnames = getfontnames()
  499.         self.fontitems = []
  500.         for n in self.fontnames:
  501.             m = MenuItem(self.fontmenu, n, "", self.selfont)
  502.             self.fontitems.append(m)
  503.         self.facemenu = Menu(self.menubar, "Style")
  504.         self.faceitems = []
  505.         for n, shortcut in STYLES:
  506.             m = MenuItem(self.facemenu, n, shortcut, self.selface)
  507.             self.faceitems.append(m)
  508.         self.facemenu.addseparator()
  509.         self.faceitem_normal = MenuItem(self.facemenu, "Normal", "N", 
  510.             self.selfacenormal)
  511.         self.sizemenu = Menu(self.menubar, "Size")
  512.         self.sizeitems = []
  513.         for n in SIZES:
  514.             m = MenuItem(self.sizemenu, `n`, "", self.selsize)
  515.             self.sizeitems.append(m)
  516.         self.sizemenu.addseparator()
  517.         self.sizeitem_bigger = MenuItem(self.sizemenu, "Bigger", "+", 
  518.             self.selsizebigger)
  519.         self.sizeitem_smaller = MenuItem(self.sizemenu, "Smaller", "-", 
  520.             self.selsizesmaller)
  521.                     
  522.     def selfont(self, id, item, *rest):
  523.         if self.active:
  524.             font = self.fontnames[item-1]
  525.             self.active.menu_setfont(font)
  526.         else:
  527.             EasyDialogs.Message("No active window?")
  528.  
  529.     def selface(self, id, item, *rest):
  530.         if self.active:
  531.             face = (1<<(item-1))
  532.             self.active.menu_modface(face)
  533.         else:
  534.             EasyDialogs.Message("No active window?")
  535.  
  536.     def selfacenormal(self, *rest):
  537.         if self.active:
  538.             self.active.menu_setface(0)
  539.         else:
  540.             EasyDialogs.Message("No active window?")
  541.  
  542.     def selsize(self, id, item, *rest):
  543.         if self.active:
  544.             size = SIZES[item-1]
  545.             self.active.menu_setsize(size)
  546.         else:
  547.             EasyDialogs.Message("No active window?")
  548.  
  549.     def selsizebigger(self, *rest):
  550.         if self.active:
  551.             self.active.menu_incsize(2)
  552.         else:
  553.             EasyDialogs.Message("No active window?")
  554.  
  555.     def selsizesmaller(self, *rest):
  556.         if self.active:
  557.             self.active.menu_incsize(-2)
  558.         else:
  559.             EasyDialogs.Message("No active window?")
  560.  
  561.     def updatemenubar(self):
  562.         changed = 0
  563.         on = (self.active <> None)
  564.         if on <> self.windowgroup_on:
  565.             for m in self.windowgroup:
  566.                 m.enable(on)
  567.             self.windowgroup_on = on
  568.             changed = 1
  569.         if on:
  570.             # only if we have an edit menu
  571.             on = self.active.have_selection()
  572.             if on <> self.focusgroup_on:
  573.                 for m in self.focusgroup:
  574.                     m.enable(on)
  575.                 self.focusgroup_on = on
  576.                 changed = 1
  577.             on = self.active.can_paste()
  578.             if on <> self.pastegroup_on:
  579.                 self.pasteitem.enable(on)
  580.                 self.pastegroup_on = on
  581.                 changed = 1
  582.             on = self.active.can_undo()
  583.             if on <> self.undo_label:
  584.                 if on:
  585.                     self.undoitem.enable(1)
  586.                     self.undoitem.settext(on)
  587.                     self.undo_label = on
  588.                 else:
  589.                     self.undoitem.settext("Nothing to undo")
  590.                     self.undoitem.enable(0)
  591.                 changed = 1
  592.             if self.updatefontmenus():
  593.                 changed = 1
  594.         if changed:
  595.             DrawMenuBar()
  596.             
  597.     def updatefontmenus(self):
  598.         info = self.active.getruninfo()
  599.         if info == self.ffs_values:
  600.             return 0
  601.         # Remove old checkmarks
  602.         if self.ffs_values == ():
  603.             self.ffs_values = (None, None, None)
  604.         font, face, size = self.ffs_values
  605.         if font <> None:
  606.             fnum = self.fontnames.index(font)
  607.             self.fontitems[fnum].check(0)
  608.         if face <> None:
  609.             for i in range(len(self.faceitems)):
  610.                 if face & (1<<i):
  611.                     self.faceitems[i].check(0)
  612.         if size <> None:
  613.             for i in range(len(self.sizeitems)):
  614.                 if SIZES[i] == size:
  615.                     self.sizeitems[i].check(0)
  616.                 
  617.         self.ffs_values = info
  618.         # Set new checkmarks
  619.         font, face, size = self.ffs_values
  620.         if font <> None:
  621.             fnum = self.fontnames.index(font)
  622.             self.fontitems[fnum].check(1)
  623.         if face <> None:
  624.             for i in range(len(self.faceitems)):
  625.                 if face & (1<<i):
  626.                     self.faceitems[i].check(1)
  627.         if size <> None:
  628.             for i in range(len(self.sizeitems)):
  629.                 if SIZES[i] == size:
  630.                     self.sizeitems[i].check(1)
  631.         # Set outline/normal for sizes
  632.         if font:
  633.             exists = getfontsizes(font, SIZES)
  634.             for i in range(len(self.sizeitems)):
  635.                 if exists[i]:
  636.                     self.sizeitems[i].setstyle(0)
  637.                 else:
  638.                     self.sizeitems[i].setstyle(8)
  639.  
  640.     #
  641.     # Apple menu
  642.     #
  643.     
  644.     def do_about(self, id, item, window, event):
  645.         EasyDialogs.Message("A simple single-font text editor based on WASTE")
  646.             
  647.     #
  648.     # File menu
  649.     #
  650.  
  651.     def open(self, *args):
  652.         self._open(0)
  653.         
  654.     def openfile(self, *args):
  655.         self._open(1)
  656.  
  657.     def _open(self, askfile):
  658.         if askfile:
  659.             fss, ok = macfs.StandardGetFile('TEXT')
  660.             if not ok:
  661.                 return
  662.             path = fss.as_pathname()
  663.             name = os.path.split(path)[-1]
  664.             try:
  665.                 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
  666.                 data = fp.read()
  667.                 fp.close()
  668.             except IOError, arg:
  669.                 EasyDialogs.Message("IOERROR: "+`arg`)
  670.                 return
  671.         else:
  672.             path = None
  673.             name = "Untitled %d"%self.num
  674.             data = ''
  675.         w = WasteWindow(self)
  676.         w.open(path, name, data)
  677.         self.num = self.num + 1
  678.  
  679.     def insertfile(self, *args):
  680.         if self.active:
  681.             fss, ok = macfs.StandardGetFile('TEXT')
  682.             if not ok:
  683.                 return
  684.             path = fss.as_pathname()
  685.             try:
  686.                 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
  687.             except IOError, arg:
  688.                 EasyDialogs.Message("IOERROR: "+`arg`)
  689.                 return
  690.             self.active.menu_insert(fp)
  691.         else:
  692.             EasyDialogs.Message("No active window?")
  693.  
  694.     def inserthtml(self, *args):
  695.         if self.active:
  696.             fss, ok = macfs.StandardGetFile('TEXT')
  697.             if not ok:
  698.                 return
  699.             path = fss.as_pathname()
  700.             try:
  701.                 fp = open(path, 'r')
  702.             except IOError, arg:
  703.                 EasyDialogs.Message("IOERROR: "+`arg`)
  704.                 return
  705.             self.active.menu_insert_html(fp)
  706.         else:
  707.             EasyDialogs.Message("No active window?")
  708.  
  709.         
  710.     def closewin(self, *args):
  711.         if self.active:
  712.             self.active.close()
  713.         else:
  714.             EasyDialogs.Message("No active window?")
  715.         
  716.     def save(self, *args):
  717.         if self.active:
  718.             self.active.menu_save()
  719.         else:
  720.             EasyDialogs.Message("No active window?")
  721.         
  722.     def saveas(self, *args):
  723.         if self.active:
  724.             self.active.menu_save_as()
  725.         else:
  726.             EasyDialogs.Message("No active window?")
  727.             
  728.         
  729.     def quit(self, *args):
  730.         for w in self._windows.values():
  731.             w.close()
  732.         if self._windows:
  733.             return
  734.         self._quit()
  735.         
  736.     #
  737.     # Edit menu
  738.     #
  739.     
  740.     def undo(self, *args):
  741.         if self.active:
  742.             self.active.menu_undo()
  743.         else:
  744.             EasyDialogs.Message("No active window?")
  745.         
  746.     def cut(self, *args):
  747.         if self.active:
  748.             self.active.menu_cut()
  749.         else:
  750.             EasyDialogs.Message("No active window?")
  751.         
  752.     def copy(self, *args):
  753.         if self.active:
  754.             self.active.menu_copy()
  755.         else:
  756.             EasyDialogs.Message("No active window?")
  757.         
  758.     def paste(self, *args):
  759.         if self.active:
  760.             self.active.menu_paste()
  761.         else:
  762.             EasyDialogs.Message("No active window?")
  763.  
  764.     def clear(self, *args):
  765.         if self.active:
  766.             self.active.menu_clear()
  767.         else:
  768.             EasyDialogs.Message("No active window?")
  769.         
  770.     #
  771.     # Other stuff
  772.     #    
  773.  
  774.     def idle(self, event):
  775.         if self.active:
  776.             self.active.do_idle(event)
  777.             
  778.     def newRuler(self, obj):
  779.         """Insert a new ruler. Make it as wide as the window minus 2 pxls"""
  780.         ted = obj.WEGetObjectOwner()
  781.         l, t, r, b = ted.WEGetDestRect()
  782.         return r-l, 4
  783.         
  784.     def drawRuler(self, (l, t, r, b), obj):
  785.         y = (t+b)/2
  786.         Qd.MoveTo(l+2, y)
  787.         Qd.LineTo(r-2, y)
  788.         return 0
  789.             
  790. class MyHTMLParser(htmllib.HTMLParser):
  791.     
  792.     def anchor_bgn(self, href, name, type):
  793.         self.anchor = href
  794.         if self.anchor:
  795.             self.anchorlist.append(href)
  796.             self.formatter.push_style('anchor')
  797.  
  798.     def anchor_end(self):
  799.         if self.anchor:
  800.             self.anchor = None
  801.             self.formatter.pop_style()
  802.  
  803.             
  804. def getfontnames():
  805.     names = []
  806.     for i in range(256):
  807.         n = Fm.GetFontName(i)
  808.         if n: names.append(n)
  809.     return names
  810.     
  811. def getfontsizes(name, sizes):
  812.     exist = []
  813.     num = Fm.GetFNum(name)
  814.     for sz in sizes:
  815.         if Fm.RealFont(num, sz):
  816.             exist.append(1)
  817.         else:
  818.             exist.append(0)
  819.     return exist
  820.  
  821. def main():
  822.     App = Wed()
  823.     App.mainloop()
  824.     
  825. if __name__ == '__main__':
  826.     main()
  827.     
  828.